package edu.zju.gis.dbfg.tileserver.scala

import geotrellis.layer.{MapKeyTransform, SpatialKey, TileLayerMetadata, ZoomedLayoutScheme}
import geotrellis.raster.MultibandTile
import geotrellis.raster.render.{ClassBoundaryType, ColorMap, ColorRamp, ColorRamps, LessThan, LessThanOrEqualTo, Png, ColorMethods}
import geotrellis.raster.{RasterExtent, _}
import geotrellis.spark.store.file.FileLayerReader
import geotrellis.store.file.{FileAttributeStore, FileValueReader}
import geotrellis.store.{AttributeStore, LayerId, ValueNotFoundError}
import org.apache.spark.SparkContext

import scala.collection.JavaConverters._

/**
  * @author Hu
  * @date 2019/11/1
  **/
object GeoTrellisTileServer {

  def reader(fileValueReader: FileValueReader, layerId: LayerId)
  = fileValueReader.reader[SpatialKey, MultibandTile](layerId)

  def getAttributeStore(storePath: String): FileAttributeStore = FileAttributeStore(storePath)

  def getReader(implicit sci: SparkContext, attributeStore: FileAttributeStore): FileLayerReader = {
    FileLayerReader(attributeStore)
  }

  def getClassBoundaryType(): ClassBoundaryType = {
    LessThanOrEqualTo
  }

  def getContinouesColorMap(startValue: Int, endValue: Int, stops: Int, cr: ColorRamp, options: ColorMap.Options): ColorMap = {
    cr.toColorMap((startValue to endValue).toArray, options)
  }

  /**
    * @param start 色彩的十六进制
    * @param end   色彩的十六进制
    * @return
    */
  def getContinouesColorMap(startValue: Int, endValue: Int, stops: Int, options: ColorMap.Options): ColorMap = {
    val cr = ColorRamps.BlueToOrange
    cr.toColorMap((startValue to endValue).toArray, options)
  }

  def getCustomContinouesColorMap(startColor: Int, endColor: Int, startValue: Int, endValue: Int, stops: Array[Double], options: ColorMap.Options): ColorMap = {
    val cr = ColorRamp(startColor, endColor).stops(stops.length)
    cr.toColorMap(stops, options)
  }

  def transformColorMap(jm: java.util.Map[java.lang.Double, java.lang.Integer], options: ColorMap.Options): ColorMap = {
    val m = jm.asScala
    var om: Map[Double, Int] = Map()
    for (t <- m) {
      val v = t._1.toDouble
      val c = t._2.toInt
      om += (v -> c)
    }
    ColorMap(om, options)
  }

  def colorMapFromStr(str: String): ColorMap = {
    ColorMap.fromStringDouble(str).get
  }

  def getDiscreteRandomColorMap(jvs: java.util.List[java.lang.Double], options: ColorMap.Options): ColorMap = {
    var m: Map[Double, Int] = Map()
    val vs = jvs.asScala.toArray.sorted
    val cr = ColorRamps.BlueToOrange.stops(vs.length)
    val colors: List[Int] = cr.toList
    for (i <- 0 until vs.length) {
      m += (vs(i).toDouble -> colors(i))
    }
    ColorMap(m, options)
  }

  def getMaxZoom(attributeStore: AttributeStore, layerName: String) = 9

  def getResampleTilePng(x: Int, y: Int, z: Int, zmax: Int, layername: String, attributeStore: AttributeStore, storePath: String, bandIndex: Int, colormap: ColorRamp): Png = {
    val layerId: LayerId = LayerId(layername, zmax)
    val rmd = attributeStore.readMetadata[TileLayerMetadata[SpatialKey]](layerId)
    val layoutLevel = ZoomedLayoutScheme(rmd.crs).levelForZoom(rmd.extent, z)
    val mapTransform = MapKeyTransform(rmd.crs, layoutLevel.layout.layoutCols, layoutLevel.layout.layoutRows)
    val targetExtent = mapTransform(x, y)
    val GridBounds(nx, ny, _, _) = rmd.mapTransform(targetExtent)
    val sourceExtent = rmd.mapTransform(nx, ny)
    val valueReader: FileValueReader = FileValueReader(storePath)
    val tileOpt: Option[MultibandTile] =
      try {
        Some(valueReader.reader[SpatialKey, MultibandTile](LayerId(layername, zmax)).read(nx, ny))
      } catch {
        case _: ValueNotFoundError =>
          None
      }
    tileOpt.map(tile => {
      return tile.band(bandIndex).resample(sourceExtent, RasterExtent(targetExtent, 256, 256)).renderPng(colormap)
    })

    null
  }

  def getResampleTilePng(x: Int, y: Int, z: Int, zmax: Int, layername: String, attributeStore: AttributeStore, storePath: String, bandIndex: Int, colormap: ColorMap): Png = {
    val layerId: LayerId = LayerId(layername, zmax)
    val rmd = attributeStore.readMetadata[TileLayerMetadata[SpatialKey]](layerId)
    val layoutLevel = ZoomedLayoutScheme(rmd.crs).levelForZoom(rmd.extent, z)
    val mapTransform = MapKeyTransform(rmd.crs, layoutLevel.layout.layoutCols, layoutLevel.layout.layoutRows)
    val targetExtent = mapTransform(x, y)
    val GridBounds(nx, ny, _, _) = rmd.mapTransform(targetExtent)
    val sourceExtent = rmd.mapTransform(nx, ny)
    val valueReader: FileValueReader = FileValueReader(storePath)
    val tileOpt: Option[MultibandTile] =
      try {
        Some(valueReader.reader[SpatialKey, MultibandTile](LayerId(layername, zmax)).read(nx, ny))
      } catch {
        case _: ValueNotFoundError =>
          None
      }
    tileOpt.map(tile => {
      return tile.band(bandIndex).resample(sourceExtent, RasterExtent(targetExtent, 256, 256), ResampleMethods.NearestNeighbor).renderPng(colormap)
    })

    null
  }

  def getTilePng(x: Int, y: Int, z: Int, layername: String, attributeStore: AttributeStore, storePath: String, bandIndex: Int, colormap: ColorRamp): Png = {
    val valueReader: FileValueReader = FileValueReader(storePath)
    val tileOpt: Option[MultibandTile] =
      try {
        Some(valueReader.reader[SpatialKey, MultibandTile](LayerId(layername, z)).read(x, y))
      } catch {
        case _: ValueNotFoundError =>
          None
      }
    tileOpt.map(tile => {
      return tile.band(bandIndex).renderPng(colormap)
    })

    null
  }

  def getTilePng(x: Int, y: Int, z: Int, layername: String, attributeStore: AttributeStore, storePath: String, bandIndex: Int, colormap: ColorMap): Png = {
    val valueReader: FileValueReader = FileValueReader(storePath)
    val tileOpt: Option[MultibandTile] =
      try {
        Some(valueReader.reader[SpatialKey, MultibandTile](LayerId(layername, z)).read(x, y))
      } catch {
        case _: ValueNotFoundError =>
          None
      }
    tileOpt.map(tile => {
      return tile.band(bandIndex).renderPng(colormap)
    })

    null
  }

  //  def getColorMapByStr(str: String): ColorMap = ColorMap.fromString(str)

  def getTileRGBPng(x: Int, y: Int, z: Int, layername: String, attributeStore: AttributeStore, storePath: String, bandIndex_1: Int, bandIndex_2: Int, bandIndex_3: Int): Png = {
    val valueReader: FileValueReader = FileValueReader(storePath)
    val sourceTile = valueReader.reader[SpatialKey, MultibandTile](LayerId(layername, z)).read(x, y);
    var sourceBand_1 = sourceTile.band(bandIndex_1)
    var sourceBand_2 = sourceTile.band(bandIndex_2)
    var sourceBand_3 = sourceTile.band(bandIndex_3)

    var b1MinMax = sourceBand_1.findMinMaxDouble
    var b2MinMax = sourceBand_2.findMinMaxDouble
    var b3MinMax = sourceBand_3.findMinMaxDouble

    var tileBand1 = sourceBand_1.normalize(b1MinMax._1, b1MinMax._2, 0, 255).convert(ByteConstantNoDataCellType)
    var tileBand2 = sourceBand_2.normalize(b2MinMax._1, b2MinMax._2, 0, 255).convert(ByteConstantNoDataCellType)
    var tileBand3 = sourceBand_3.normalize(b3MinMax._1, b3MinMax._2, 0, 255).convert(ByteConstantNoDataCellType)

    val mmultiTIle = MultibandTile.apply(tileBand1, tileBand2, tileBand3)

    val colorTile = mmultiTIle.color

    return colorTile.renderPng()

    null
  }

  def getTile(tileOpt: Option[MultibandTile], index: Int): Tile = {
    tileOpt.map(tile => {
      return tile.band(index)
    })
    null
  }

}
